#pragma once

#include "Common.hpp"
#include "CentralCache.hpp"

class ThreadCache
{
public:
    // 申请内存
    void *Allocate(size_t bytes)
    {
        assert(bytes <= MAX_BYTES);                  // 向ThreadCache最多申请 256*1024 大小的内存空间
        size_t alignNum = SizeClass::RoundUp(bytes); // 先计算对齐数
        size_t index = SizeClass::Index(bytes);      // 找到所在桶

        if (!free_list_[index].empty()) // 所在桶不为空，就直接获取内存
            return free_list_[index].pop();
        else // 所在桶为空，从 CentralCache 中获取内存，并且返回
            return FetchFromCentralCache(index, alignNum);
    }

    // 释放内存(传入size，是为了知道还回来的内存放入哪个桶)
    void Deallocate(void *ptr, size_t size)
    {
        assert(ptr);
        assert(size <= MAX_BYTES);

        size_t index = SizeClass::Index(size); // 计算所在桶
        free_list_[index].push(ptr);           // 放入自由链表

        // 如果自由链表长度太长，就将内存对象还给CentralCache
        if (free_list_[index].Size() >= free_list_[index].maxsize())
            ListTooLong(free_list_[index], size);
    }

    // ThreadCache 从中心缓存CentralCache 获取对象，CentralCache可能会一次性返回多个对象
    void *FetchFromCentralCache(size_t index, size_t size)
    {
        // 从CentralCache一次性获取多少个
        size_t batchNum = std::min(SizeClass::NumMoveSize(size), free_list_[index].maxsize());

        if (batchNum == free_list_[index].maxsize())
            free_list_[index].maxsize() += 1;

        void *start = nullptr;
        void *end = nullptr;
        size_t actulNum = CentralCache::GetInstance()->FetchRangeObj(start, end, batchNum, size);
        assert(actulNum >= 1); // 至少获得一个

        if (actulNum == 1) // 如果只从 CentralCache 获取到一个，就直接将这一个返回
        {
            assert(start == end);
            return start;
        }
        else // 如果从 CentralCache 获取到多个，就将第一个返回，其余的内存挂回自由链表
        {
            free_list_[index].pushRange(*(void **)start, end, actulNum - 1);
            return start;
        }
    }

    void ListTooLong(FreeList &list, size_t size)
    {
        void *start = nullptr;
        void *end = nullptr;
        list.popRange(start, end, list.maxsize());

        // 将多余的内存对象还给 CentralCache
        CentralCache::GetInstance()->ReleaseListToSpans(start, size);
    }

private:
    FreeList free_list_[NFREELIST];
};

static _declspec(thread) ThreadCache *pTLSThreadCache = nullptr;  //使每个线程都有一个属于自己的 ThreadCache，这是Windows下的 TLS(线程本地存储)
//static __thread ThreadCache *pTLSThreadCache = nullptr; // 使每个线程都有一个属于自己的 ThreadCache，这是Linux下的 TLS(线程本地存储) __thread为关键字